package com.ybwh.datasource.refreshable.core;


import lombok.extern.slf4j.Slf4j;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;

/**
 * <p>
 * 委托方式实现的可刷新资源.
 * </p>
 *
 * @author fanbeibei
 * @version V1.0
 * @since 2019/4/5 21:41
 */
@Slf4j
public abstract class AbstractDelegatedRefreshResource<T> implements RefreshableResource {
    /**
     * 旧Datasource存活时间
     */
    protected static final long OLD_RESOURCE_IDEL = 60 * 60 * 1000;

    protected long oldResourceIdel = OLD_RESOURCE_IDEL;


    private volatile boolean inited = false;
    private volatile boolean closed = false;

    protected final ResourceHolder<T> resourceHolder = new ResourceHolder();


    /**
     * 时间戳 -> DataSource
     */
    protected final ConcurrentHashMap<Long, T> oldResourceMap = new ConcurrentHashMap<>();

    protected ScheduledExecutorService closeResourceScheduledService = new ScheduledThreadPoolExecutor(1, new ThreadFactory() {
        private final AtomicLong threadIndex = new AtomicLong(0);
        private final String THREAD_NAME_PREFIX = "AbstractDelegatedRefreshResource-thread-";

        @Override
        public Thread newThread(Runnable r) {
            return new Thread(r, THREAD_NAME_PREFIX + this.threadIndex.incrementAndGet());

        }
    });

    protected AbstractDelegatedRefreshResource(){

        init();
    }

    protected AbstractDelegatedRefreshResource(long oldResourceIdel){
        this.oldResourceIdel = oldResourceIdel;
        init();
    }

    /**
     * 初始化逻辑
     */
    public synchronized void init() {
        //已经做过初始化
        if (inited) {
            return;
        }

        inited = true;

        try {
            T  newResource = createResource();
            if(null != newResource){
                resourceHolder.set(newResource);
            }else{
                log.info("init error!! createResource failed");
            }

            closeResourceScheduledService.scheduleAtFixedRate(() -> releaseOldResources(),
                    oldResourceIdel / 2, oldResourceIdel / 2, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            log.error("init exception!", e);
        }
    }


    /**
     * 销毁释放资源逻辑
     */
    public synchronized void destroy() {
        if (closed) {
            return;
        }

        closed = true;

        try {
            closeResourceScheduledService.shutdown();
            destroyResource(resourceHolder.get());
            releaseOldResources();
        } catch (Exception e) {
            log.error("destroy resource error", e);
        }
    }


    @Override
    public synchronized void refresh() throws SQLException {
        beforeRefresh();

        log.info("refresh method invoke!!");
        T  newResource = createResource();
        if(null != newResource){
            resourceHolder.set(newResource);
            //这里refresh执行频率很低且是串行执行的，所以不可能造成oldDataSourceMap的key覆盖
            oldResourceMap.put(System.currentTimeMillis(), resourceHolder.get());

        }else{
            log.error("refresh error!! createResource failed!!");
        }

        afterRefresh();
    }


    /**
     * 销毁旧数据源
     */
    private void releaseOldResources() {
        log.debug("releaseOldResources invoke! ");
        /**
         * 定期延时关闭老的Resource,由于这里Resource虽被刷新替换,但人有可能有业务线程（比如批量导入）在使用，
         * 所以需要延期关闭。
         *
         */
        for (Map.Entry<Long, T> entry : oldResourceMap.entrySet()) {
            T oldResource = entry.getValue();
            Long replacedTimestamp = entry.getKey();
            long idelTime = System.currentTimeMillis() - replacedTimestamp;

            if (idelTime >= OLD_RESOURCE_IDEL) {


                oldResourceMap.remove(replacedTimestamp);
                try {
                    log.debug("destroy resource,{}", oldResource.toString());
                    destroyResource(oldResource);
                } catch (Exception e) {
                    log.error("destroy resource error", e);
                }

            }

        }
    }

    /**
     * 刷新前(refresh执行前)执行逻辑
     */
    @Override
    public void beforeRefresh() {

    }

    /**
     * 刷新后(refresh执行后)执行逻辑
     */
    @Override
    public void afterRefresh() {

    }


    /**
     * 创建资源,异常需自行处理
     *
     * @return null创建失败
     */
    protected abstract T createResource();

    /**
     * 销毁资源
     *
     * @param resource 资源
     * @throws Exception 销毁失败
     */
    protected abstract void destroyResource(T resource) throws Exception;


}
